﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Dynamic;
using System.Management.Automation;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace LinqToPowershell
{   
    [Cmdlet("Where", "LinqObject")]
    public class WhereCmdlet : LazyCmdlet
    {
        [Parameter(ParameterSetName="String", Mandatory = true, Position = 0)]
        public string PredicateString { get; set; }

        [Parameter(ParameterSetName = "ScriptBlock", Mandatory = true, Position = 0)]
        public ScriptBlock PredicateLambda { get; set; }

        protected override QueryableWrapper Process()
        {
            if (ParameterSetName == "String")
            {
                return new QueryableWrapper(Input.Query.Where(PredicateString));
            }
            else
            {
                var typeArgs = Input.GetTypeArgs();
                if (typeArgs == null)
                    throw new ArgumentException("You must pass a generic QueryableWrapper<T> in order to use scriptblock lambdas");

                var genericWhere = GenericHelper.Create<WhereCmdlet>(typeArgs, this);
                return genericWhere.InvokeMethod<QueryableWrapper>("ProcessGeneric");
            }
        }
    }

    public class WhereCmdlet<T> : WhereCmdlet
    {        
        public WhereCmdlet(WhereCmdlet copy)
        {
            PredicateLambda = copy.PredicateLambda;
            Input = (QueryableWrapper<T>) copy.Input.ToGeneric<QueryableWrapper, T>(Input);
        }

        public QueryableWrapper<T> ProcessGeneric()
        {   
            var expression = ParseLambda();

            return new QueryableWrapper<T>(Input.Query.Where(expression));
        }

        private Expression<Func<T, bool>> ParseLambda()
        {
            Collection<PSParseError> errors;
            var tokens = PSParser.Tokenize(PredicateLambda.ToString(), out errors);
            if (errors != null && errors.Count > 0)
                throw new PSArgumentException(errors.Aggregate("Parse error:\n", (s, e) => string.Format("{0}\n{1}: {2}\n", s, e.Message, e.Token)));

            foreach (var token in tokens)
            {
                switch (token.Type)
                {
                    default:
                        break;
                }
            }
            throw new NotImplementedException();
        }

        public new QueryableWrapper<T> Input;
    }

    [Cmdlet("Sort", "LinqObject")]
    public class SortCmdlet : LazyCmdlet
    {
        [Parameter(ParameterSetName = "String", Mandatory = true, Position = 0)]
        public string OrderingString { get; set; }

        [Parameter(ParameterSetName = "ScriptBlock", Mandatory = true, Position = 0)]
        public ScriptBlock OrderingLambda { get; set; }

        protected override QueryableWrapper Process()
        {
            if (ParameterSetName == "String")
            {
                return new QueryableWrapper(Input.Query.OrderBy(OrderingString));
            }
            else
            {
                return new QueryableWrapper(Input.Query.OrderBy(OrderingString));
            }
        }
    }

    [Cmdlet("Select", "LinqObject")]
    public class SelectCmdlet : LazyCmdlet
    {
        [Parameter(ParameterSetName = "String", Mandatory = true, Position = 0)]
        public string SelectorString { get; set; }

        [Parameter(ParameterSetName = "ScriptBlock", Mandatory = true, Position = 0)]
        public ScriptBlock SelectorLambda { get; set; }

        protected override QueryableWrapper Process()
        {
            if (ParameterSetName == "String")
            {
                return new QueryableWrapper(Input.Query.Select(SelectorString));
            }
            else
            {
                return new QueryableWrapper(Input.Query.Select(SelectorString));
            }
        }
    }

    [Cmdlet("Group", "LinqObject")]
    public class GroupCmdlet : LazyCmdlet
    {
        [Parameter(ParameterSetName = "String", Mandatory = true, Position = 0)]
        public string KeySelectorString { get; set; }
        [Parameter(ParameterSetName = "String", Mandatory = true, Position = 1)]
        public string ElementSelectorString { get; set; }
        
        [Parameter(ParameterSetName = "ScriptBlock", Mandatory = true, Position = 0)]
        public ScriptBlock KeySelectorLambda { get; set; }
        [Parameter(ParameterSetName = "ScriptBlock", Mandatory = true, Position = 1)]
        public ScriptBlock ElementSelectorLambda { get; set; }

        protected override QueryableWrapper Process()
        {
            if (ParameterSetName == "String")
            {
                return new QueryableWrapper(Input.Query.GroupBy(KeySelectorString, ElementSelectorString));
            }
            else
            {
                return new QueryableWrapper(Input.Query.GroupBy(KeySelectorString, ElementSelectorString));
            }
        }
    }

    [Cmdlet("Take", "LinqObject")]
    public class TakeCmdlet : LazyCmdlet
    {
        [Parameter(Mandatory = true, Position = 0)]
        public int Count { get; set; }

        protected override QueryableWrapper Process()
        {
            return new QueryableWrapper(Input.Query.Take(Count));
        }
    }

    [Cmdlet("Skip", "LinqObject")]
    public class SkipCmdlet : LazyCmdlet
    {
        [Parameter(Mandatory = true, Position = 0)]
        public int Count { get; set; }

        protected override QueryableWrapper Process()
        {
            return new QueryableWrapper(Input.Query.Skip(Count));
        }
    }
}
